余烬缀记

Rust Tokio 文件流踩坑指南

edited on:

本文记录最近使用 Rust Tokio 从文件流中读取指定范围的内容遇到的一些问题

读取流的写法:

use tokio_stream::StreamExt;
use tokio_util::io::ReaderStream;

let file = tokio::fs::File::open("path/example.txt").await?;
let mut stream = ReaderStream::new(file);
while let Some(bytes) = stream.next().await {
	println("{:?}", &bytes?[..])
}

指定起始位置

指定起始位置有两种方式实现,Fileseek 方法 和 Streamskip 方法,这里记录一下两种方式的写法

# tokio::io::Seek

Fileseek 方法是由 tokio::io::AsyncSeekExt 特征扩展而来,这个方法可以将文件指针移动到指定的位置

use tokio_stream::StreamExt;
use tokio_util::io::ReaderStream;
use tokio::io::SeekFrom;

let mut file = tokio::fs::File::open("path/example.txt").await?;
file.seek(SeekFrom::Start(3)).await?;
let mut stream = ReaderStream::new(file);
while let Some(bytes) = stream.next().await {
	println("{:?}", &bytes?[..])
}

注意:如果使用 try_clone 克隆后再使用 seek 则会同时影响到两个文件实例,举个例子:比如我准备了三个流,每个流负责读取文件的一部分,在将 file 转为 stream 的时候需要转移所有权,因此必须使用 try_clone 来克隆 file 实例。而由于 seek 方法会影响所有的 file 实例,所以这种方式就走不通,

# tokio::stream::Skip

Streamskip 方法由 tokio::stream::StreamExt 特征扩展,这个方法同 iterskip 一致, 它将跳过前面第 n 项后创建一个新的 Stream

use tokio_stream::StreamExt;
use tokio_util::io::ReaderStream;

let file = tokio::fs::File::open("path/example.txt").await?;
let mut stream = ReaderStream::new(file).skip(3);
while let Some(bytes) = stream.next().await {
	println("{:?}", &bytes?[..])
}

需要注意的是,ReaderStream 默认缓冲区的大小是 4096 ,这个缓冲区即每项,这意味着上面的 skip 方法将跳过 3 * 4096 字节,并不是 3 字节